/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.test.gwt.tests;

import com.google.gwt.dom.client.Element;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;

import fr.openparts.OpenParts.client.OPBinding;
import fr.openparts.OpenParts.client.OPCommandCallback;
import fr.openparts.OpenParts.client.OPCommandFailureReason;
import fr.openparts.OpenParts.client.OPMessageFromEmbeddedViewHandler;
import fr.openparts.OpenParts.client.OPMessagesDispatcher;
import fr.openparts.OpenParts.client.OPParams;
import fr.openparts.OpenParts.client.OPPartsLoader;
import fr.openparts.OpenParts.client.OPScheduler;
import fr.openparts.OpenParts.client.OPShell;
import fr.openparts.OpenParts.client.OPSlot;
import fr.openparts.OpenParts.test.gwt.dummyPart.client.DummyPart;

/**
 * GWT tests for the dispatching of embedded view messages in OPShell.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 11 July 2010
 */
public class OPProxyMessagesDispatchingTestGwt extends GWTTestCase {

    private static final String SERVICE_NAME = "dummyService";
    private static final String RESOURCE_NAME = "dummyResource";
    private static final String ACTION_NAME = "dummyAction3";
    private static final String ACTION_NAME_2 = "dummyAction4";
    private static final String ACTION_NAME_3 = "dummyAction5";
    private static final String SLOT_ID = "slotId_proxyMessages";
    private static final String MESSAGE = "message";

    private static final int TIMER_TIMEOUT = 5500;
    private static final int TEST_TIMEOUT = 6000;

    private DummyPart dummyPart;
    private OPSlot slot;
    private OPParams params;
    private OPCommandCallback callback;

    private int onSuccessCalled = 0;
    private int onFailCalled = 0;
    private OPCommandFailureReason onFailReason;

    private Timer timer;

    private int slotHandlerCalls = 0;
    private int slotHandler2Calls = 0;

    /**
     * Return the name of the tested GWT Module.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#getModuleName()
     */
    @Override
    public final String getModuleName() {
        return "fr.openparts.OpenParts.test.gwt.TestOpenParts";
    }

    /**
     * Called at the start of each test to set up the part.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#gwtSetUp()
     */
    @Override
    protected void gwtSetUp() throws Exception {
        super.gwtSetUp();
        OPPartsLoader.bind();
        OPScheduler.bind();
        dummyPart = new DummyPart();
        dummyPart.bind();

        slot = OPShell.getSlot(SLOT_ID);
        slot.getViewProxy().addMessageHandler(new OPMessageFromEmbeddedViewHandler() {
            @Override
            public void handleMessageFromEmbeddedView(String message, OPParams params) {
                ++slotHandlerCalls;
            }
            @Override
            public void onEmbeddedViewAttach() {
            }
        });

        params = new OPParams("name", "value");
        callback = new OPCommandCallback() {
            @Override
            public void onSuccess() {
                ++onSuccessCalled;
            }

            @Override
            public void onFail(final OPCommandFailureReason reason) {
                ++onFailCalled;
                onFailReason = reason;
            }
        };
    }

    /**
     * Called at the end of each test to clean up the mess.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#gwtTearDown()
     */
    @Override
    protected final void gwtTearDown() throws Exception {
        super.gwtTearDown();
        unbindOPPartsLoader(OPShell.opBindingSharedInstance());
        unbindOPScheduler(OPShell.opBindingSharedInstance());
        RootPanel.getBodyElement().removeChild(slot.getElement());
        slot = null;
        onSuccessCalled = 0;
        onFailCalled = 0;
        onFailReason = null;
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        slotHandlerCalls = 0;
        slotHandler2Calls = 0;
    }

    /**
     * Test dispatching message from slot to view.
     */
    public final void testDispatchingMessageFromSlotToView() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, null);

        assertEquals("message handler by index should have been called now", 1, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);
    }

    /**
     * Test dispatching message from slot to view with params.
     */
    public final void testDispatchingMessageFromSlotToViewWithParams() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params);

        assertEquals("message handler by index should have been called now", 1, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);
    }

    /**
     * Test dispatching message from slot to view with callback.
     */
    public final void testDispatchingMessageFromSlotToViewWithCallback() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params, callback);

        assertEquals("message handler by index should have been called now", 1, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from slot to view with two messages handlers.
     */
    public final void testDispatchingMessageFromSlotToViewWithTwoHandlers() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME_2, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("message handler 1 should have been called now", 1, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should have been called now", 1, dummyPart.messageHandler2Calls);
    }

    /**
     * Test dispatching message from slot to first then second view.
     */
    public final void testDispatchingMessageFromSlotToFirstAndSecondEmbeddedView() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        final String SLOT_ID_2 = "slotId2";
        OPSlot slot2 = OPShell.getSlot(SLOT_ID_2);
        RootPanel.getBodyElement().appendChild(slot2.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID_2);

        assertEquals("there should be two message handlers", 2, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("first message handlers should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("second message handlers should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.get(1).intValue());

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params);

        assertEquals("first message handlers should have been called now", 1, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("second message handlers should still not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.get(1).intValue());

        slot2.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params);

        assertEquals("first message handlers should have been called once", 1, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("second message handlers should have been called now", 1, dummyPart.messageHandlerCallsByIndex.get(1).intValue());

        RootPanel.getBodyElement().removeChild(slot2.getElement());
    }

    /**
     * Test dispatching message from slot to view when slot is not attached to the page. Should fail with a NO_SUCH_ID_ON_THE_PAGE.
     */
    public final void testDispatchingMessageFromSlotToViewWithSlotNotAttachedToThePage() {
        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params, callback);

        assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
        assertEquals("onFail should have been called now", 1, onFailCalled);
        assertNotNull("reason shouldn't be null", onFailReason);
        assertEquals("reason should be no such id on the page", OPCommandFailureReason.TYPE_NO_SUCH_ID_IN_THE_PAGE, onFailReason.getType());
        assertNotNull("reason params shouldn't be null", onFailReason.getParams());
        assertEquals("reason slot id should be " + SLOT_ID, SLOT_ID, onFailReason.getParams().getParamValue(OPCommandFailureReason.PARAM_SLOT_ID));
    }

    /**
     * Test dispatching message from slot to view when id is not attached to a slot. Should fail with a GIVEN_ID_IS_NOT_A_SLOT.
     */
    public final void testDispatchingMessageFromSlotToViewWithIdNotAttachedToASlot() {
        final Element fakeSlot = DOM.createDiv();
        fakeSlot.setId(SLOT_ID);
        RootPanel.getBodyElement().appendChild(fakeSlot);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params, callback);

        assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
        assertEquals("onFail should have been called now", 1, onFailCalled);
        assertNotNull("reason shouldn't be null", onFailReason);
        assertEquals("reason should given id is not a slot", OPCommandFailureReason.TYPE_GIVEN_ID_IS_NOT_A_SLOT, onFailReason.getType());
        assertNotNull("reason params shouldn't be null", onFailReason.getParams());
        assertEquals("reason slot id should be " + SLOT_ID, SLOT_ID, onFailReason.getParams().getParamValue(OPCommandFailureReason.PARAM_SLOT_ID));

        RootPanel.getBodyElement().removeChild(fakeSlot);
    }

    /**
     * Test dispatching message from slot to view when no view is attached. Should fail with a NO_VIEW_ATTACHED_TO_PROXY.
     */
    public final void testDispatchingMessageFromSlotToViewWithNoViewAttachedToSlot() {
        RootPanel.getBodyElement().appendChild(slot.getElement());

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params, callback);

        assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
        assertEquals("onFail should have been called now", 1, onFailCalled);
        assertNotNull("reason shouldn't be null", onFailReason);
        assertEquals("reason should be no view attached to proxy", OPCommandFailureReason.TYPE_NO_VIEW_ATTACHED_TO_PROXY, onFailReason.getType());
        assertNull("reason params should be null", onFailReason.getParams());
    }

    /**
     * Test dispatching message from slot to view with null message. Should raise an assertion error.
     */
    public final void testDispatchingMessageFromSlotToViewWithNullMessage() {
        if (assertionsAreEnabled()) {
            try {
                slot.getViewProxy().dispatchMessageToEmbeddedView(null, params);
                fail("OPSlotProxy should raise an assertion error when trying to dispatch a null message.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test dispatching message from slot to view, with null params.
     */
    public final void testDispatchingMessageFromSlotToViewWithNullParams() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, null, callback);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from slot to view, with empty params.
     */
    public final void testDispatchingMessageFromSlotToViewWithEmptyParams() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, new OPParams(), callback);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from slot to view, with delay in loading service..
     */
    public final void testDispatchingMessageFromSlotToViewWithDelayInLoadingService() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params, callback);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("message handler by index should have been called now", 1, dummyPart.messageHandlerCallsByIndex.get(0).intValue());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from slot to view, with error in loading service..
     */
    public final void testDispatchingMessageFromSlotToViewWithErrorInLoadingService() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        slot.getViewProxy().dispatchMessageToEmbeddedView(MESSAGE, params, callback);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        assertEquals("message handler by index should not have been called yet", 0, dummyPart.messageHandlerCallsByIndex.size());
        assertEquals("message handler 1 should not have been called yet", 0, dummyPart.messageHandler1Calls);
        assertEquals("message handler 2 should not have been called yet", 0, dummyPart.messageHandler2Calls);

        timer = new Timer() {
            public void run() {
                assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
                assertEquals("onFail should have been called now", 1, onFailCalled);
                assertNotNull("reason shouldn't be null", onFailReason);
                assertEquals("reason should be no view attached to proxy", OPCommandFailureReason.TYPE_NO_VIEW_ATTACHED_TO_PROXY, onFailReason.getType());
                assertNull("reason params should be null", onFailReason.getParams());
                finishTest();
            }
        };

        timer.schedule(TIMER_TIMEOUT);
        delayTestFinish(TEST_TIMEOUT);
    }

    /**
     * Test dispatching message from view to slot.
     */
    public final void testDispatchingMessageFromViewToSlot() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("slot handler should not have been called yet", 0, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, null);

        assertEquals("slot handler should have been called now", 1, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);
    }

    /**
     * Test dispatching message from view to slot with params.
     */
    public final void testDispatchingMessageFromViewToSlotWithParams() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("slot handler should not have been called yet", 0, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params);

        assertEquals("slot handler should have been called now", 1, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);
    }

    /**
     * Test dispatching message from view to slot with callback.
     */
    public final void testDispatchingMessageFromViewToSlotWithCallback() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        assertEquals("slot handler should not have been called yet", 0, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params, callback);

        assertEquals("slot handler should have been called now", 1, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from view to slot with two messages handlers.
     */
    public final void testDispatchingMessageFromViewToSlotWithTwoHandlers() {
        slot.getViewProxy().addMessageHandler(new OPMessageFromEmbeddedViewHandler() {
            @Override
            public void handleMessageFromEmbeddedView(String message, OPParams params) {
                ++slotHandler2Calls;
            }
            @Override
            public void onEmbeddedViewAttach() {                
            }
        });

        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("slot handler should not have been called yet", 0, slotHandlerCalls);
        assertEquals("slot handler 2 should not have been called yet", 0, slotHandler2Calls);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params);

        assertEquals("slot handler should have been called now", 1, slotHandlerCalls);
        assertEquals("slot handler 2 should have been called now", 1, slotHandler2Calls);
    }

    /**
     * Test dispatching message from view to first then second slot.
     */
    public final void testDispatchingMessageFromViewToFirstAndSecondSlot() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        final String SLOT_ID_2 = "slotId2_proxyMessages";
        final OPSlot slot2 = OPShell.getSlot(SLOT_ID_2);
        slot2.getViewProxy().addMessageHandler(new OPMessageFromEmbeddedViewHandler() {
            @Override
            public void handleMessageFromEmbeddedView(String message, OPParams params) {
                ++slotHandler2Calls;
            }
            @Override
            public void onEmbeddedViewAttach() {                
            }
        });
        RootPanel.getBodyElement().appendChild(slot2.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID_2);

        assertEquals("first slot handler should not have been called yet", 0, slotHandlerCalls);
        assertEquals("second slot handler should not have been called yet", 0, slotHandler2Calls);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params);

        assertEquals("first slot handler should have been called now", 1, slotHandlerCalls);
        assertEquals("second slot handler should still not have been called yet", 0, slotHandler2Calls);

        dummyPart.slotProxyByIndex.get(1).dispatchMessageToContainerSlot(MESSAGE, params);

        assertEquals("first slot handler should have been called once", 1, slotHandlerCalls);
        assertEquals("second slot handler should have been called now", 1, slotHandler2Calls);

        RootPanel.getBodyElement().removeChild(slot2.getElement());
    }

    /**
     * Test dispatching message from view to slot when slot is not attached to the page. Should fail with a NO_SUCH_ID_ON_THE_PAGE.
     */
    public final void testDispatchingMessageFromViewToSlotWithSlotNotAttachedToThePage() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);
        RootPanel.getBodyElement().removeChild(slot.getElement());

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params, callback);

        assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
        assertEquals("onFail should have been called now", 1, onFailCalled);
        assertNotNull("reason shouldn't be null", onFailReason);
        assertEquals("reason should be no such id on the page", OPCommandFailureReason.TYPE_NO_SUCH_ID_IN_THE_PAGE, onFailReason.getType());
        assertNotNull("reason params shouldn't be null", onFailReason.getParams());
        assertEquals("reason slot id should be " + SLOT_ID, SLOT_ID, onFailReason.getParams().getParamValue(OPCommandFailureReason.PARAM_SLOT_ID));
    }

    /**
     * Test dispatching message from view to slot when id is not attached to a slot. Should fail with a GIVEN_ID_IS_NOT_A_SLOT.
     */
    public final void testDispatchingMessageFromViewToSlotWithIdNotAttachedToASlot() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        RootPanel.getBodyElement().removeChild(slot.getElement());
        final Element fakeSlot = DOM.createDiv();
        fakeSlot.setId(SLOT_ID);
        RootPanel.getBodyElement().appendChild(fakeSlot);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params, callback);

        assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
        assertEquals("onFail should have been called now", 1, onFailCalled);
        assertNotNull("reason shouldn't be null", onFailReason);
        assertEquals("reason should given id is not a slot", OPCommandFailureReason.TYPE_GIVEN_ID_IS_NOT_A_SLOT, onFailReason.getType());
        assertNotNull("reason params shouldn't be null", onFailReason.getParams());
        assertEquals("reason slot id should be " + SLOT_ID, SLOT_ID, onFailReason.getParams().getParamValue(OPCommandFailureReason.PARAM_SLOT_ID));

        RootPanel.getBodyElement().removeChild(fakeSlot);
    }

    /**
     * Test dispatching message from view to slot when no slot is attached. Should fail with a NO_SLOT_ATTACHED_TO_PROXY.
     */
    public final void testDispatchingMessageFromViewToSlotWithNoSlotAttachedToSlot() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);
        registerDummyProxyInDispatcher(OPMessagesDispatcher.getDispatcherAttachedToId(SLOT_ID));

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, params, callback);

        assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
        assertEquals("onFail should have been called now", 1, onFailCalled);
        assertNotNull("reason shouldn't be null", onFailReason);
        assertEquals("reason should be no slot attached to proxy", OPCommandFailureReason.TYPE_NO_SLOT_ATTACHED_TO_PROXY, onFailReason.getType());
        assertNull("reason params should be null", onFailReason.getParams());
    }

    /**
     * Test dispatching message from view to slot with null message. Should raise an assertion error.
     */
    public final void testDispatchingMessageFromViewToSlotWithNullMessage() {
        if (assertionsAreEnabled()) {
            RootPanel.getBodyElement().appendChild(slot.getElement());
            OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
            OPShell.notifyLoadingCompleted(SERVICE_NAME);

            try {
                dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(null, params);
                fail("OPSlotProxy should raise an assertion error when trying to dispatch a null message.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test dispatching message from view to slot, with null params.
     */
    public final void testDispatchingMessageFromViewToSlotWithNullParams() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, null, callback);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from view to slot, with empty params.
     */
    public final void testDispatchingMessageFromViewToSlotWithEmptyParams() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME, SLOT_ID);
        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        dummyPart.slotProxyByIndex.get(0).dispatchMessageToContainerSlot(MESSAGE, new OPParams(), callback);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from view to slot, with delay in loading service..
     */
    public final void testDispatchingMessageFromViewToslotWithDelayInLoadingService() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        dummyPart.callback = callback;

        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME_3, SLOT_ID);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        OPShell.notifyLoadingCompleted(SERVICE_NAME);

        assertEquals("onSuccess should have been called now", 1, onSuccessCalled);
        assertEquals("onFail should still not have been called", 0, onFailCalled);
    }

    /**
     * Test dispatching message from view to slot, with error in loading service..
     */
    public final void testDispatchingMessageFromViewToSlotWithErrorInLoadingService() {
        RootPanel.getBodyElement().appendChild(slot.getElement());
        dummyPart.callback = callback;

        OPShell.displayView(SERVICE_NAME, RESOURCE_NAME, ACTION_NAME_3, SLOT_ID);

        assertEquals("onSuccess should not have been called yet", 0, onSuccessCalled);
        assertEquals("onFail should not have been called yet", 0, onFailCalled);

        timer = new Timer() {
            public void run() {
                // displayView shouldn't have been called, so slotproxy shouldn't have been created, so no way for callback to have been called
                assertEquals("onSuccess should still not have been called", 0, onSuccessCalled);
                assertEquals("onFail should still have not been called", 0, onFailCalled);
                finishTest();
            }
        };

        timer.schedule(TIMER_TIMEOUT);
        delayTestFinish(TEST_TIMEOUT);
    }

    /**
     * Register a dummy in the dispatcher.
     * 
     * @param dispatcher
     *            the dispatcher on which to register the dummy.
     */
    private final native void registerDummyProxyInDispatcher(final OPMessagesDispatcher dispatcher) /*-{
        dispatcher.messageFromEmbeddedViewHandlerCallback = null;
    }-*/;

    /**
     * Unbind the OPScheduler.
     */
    private final native void unbindOPScheduler(final OPBinding opbinding) /*-{
        opbinding.scheduler = null;
    }-*/;

    /**
     * Unbind the OPPartsLoader.
     */
    private final native void unbindOPPartsLoader(final OPBinding binding) /*-{
        binding.partsLoader = null;
    }-*/;

    /**
     * Check if assertions are enabled.
     * 
     * @return true if assertions are enabled, false otherwise
     */
    private final boolean assertionsAreEnabled() {
        try {
            assert false;
            return false;
        } catch (AssertionError e) {
            return true;
        }
    }

}
